#include "ch32v30x.h"
#include "ch32v30x_i2c.h"
#include "ch32v30x_gpio.h"
#include "ch32v30x_rcc.h"
#include "core_riscv.h"
#include <stdint.h>
#define LOG_TAG "[HAL_IIC]"
#include "hal_iic.h"
#define MAX_IIC_DEV_NUM 2
#define GPIO_TYPEDEF_TO_PORT_NUM(addr) (((int)addr)-APB2PERIPH_BASE / 0x400)

struct CH32V30x_I2C_Init_info 
{
    I2C_TypeDef * i2c_dev_handle;
    GPIO_TypeDef * SDA_GPIO_Port;
    GPIO_TypeDef * SCL_GPIO_Port;
    uint32_t SDA_GPIO_Pin;
    uint32_t SCL_GPIO_Pin;
    uint32_t I2C_RCC_APB1Periph_id;
    uint32_t SDA_RCC_APB2Periph_id;
    uint32_t SCL_RCC_APB2Periph_id;
};

static const struct CH32V30x_I2C_Init_info i2c_dev_info[2] = 
{
    {
        .i2c_dev_handle    = I2C1,
        .I2C_RCC_APB1Periph_id = RCC_APB1Periph_I2C1,
        .SCL_GPIO_Pin      = GPIO_Pin_6,
        .SCL_GPIO_Port     = GPIOB,
        .SCL_RCC_APB2Periph_id = RCC_APB2Periph_GPIOB,
        .SDA_GPIO_Pin      = GPIO_Pin_7,
        .SDA_GPIO_Port     = GPIOB,
        .SDA_RCC_APB2Periph_id = RCC_APB2Periph_GPIOB,
    },
    {
        .i2c_dev_handle    = I2C2,
        .I2C_RCC_APB1Periph_id = RCC_APB1Periph_I2C2,
        .SCL_GPIO_Pin      = GPIO_Pin_10,
        .SCL_GPIO_Port     = GPIOB,
        .SCL_RCC_APB2Periph_id = RCC_APB2Periph_GPIOB,
        .SDA_GPIO_Pin      = GPIO_Pin_11,
        .SDA_GPIO_Port     = GPIOB,
        .SDA_RCC_APB2Periph_id = RCC_APB2Periph_GPIOB,
    },
};
static error_t hal_iic_get_dev_addr_len(I2C_TypeDef *dev)
{
    if(dev == NULL)
    {
        return -ESPIPE;
    }
    int ret = -1 ;
    switch(dev->OADDR1 & (I2C_AcknowledgedAddress_7bit | I2C_AcknowledgedAddress_10bit)){
        case I2C_AcknowledgedAddress_7bit:
            ret = 7;
            break;
        case I2C_AcknowledgedAddress_10bit:
            ret = 10;
            break;
        default:
            ret = -EINVAL;
            break;
    }
    return ret ;
}
static error_t ch32v30x_i2c_wait_event(I2C_TypeDef *i2c_dev, uint32_t event)
{
    uint16_t counter = 0xffff;
    while(!I2C_CheckEvent(i2c_dev, event) && counter)
    {
        counter--;
    }
    if(counter)
    {
        return 0;
    }
    else
    {
        return -EBUSY;
    }
}
static error_t ch32v30x_i2c_wait_tx_free(I2C_TypeDef *i2c_dev)
{
    uint16_t counter = 0xffff;
    while(!I2C_CheckEvent(i2c_dev, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && counter)
    {
        counter--;
    }
    if(counter)
    {
        return 0;
    }
    else {
        return -EBUSY;
    }
}
static error_t ch32v30x_i2c_wait_rx_complete(I2C_TypeDef *i2c_dev)
{
    uint16_t counter = 0xffff;
    while(!I2C_CheckEvent(i2c_dev, I2C_EVENT_MASTER_BYTE_RECEIVED) && counter)
    {
        --counter;
    }
    if(counter)
    {
        return 0;
    }
    else
    {
        return -EBUSY;
    }
}

error_t hal_iic_init(int iic_id, int baud){
    if(iic_id > MAX_IIC_DEV_NUM - 1)
    {
        LOGE("id exceed max iic dev num");
        return -ENXIO;
    }
    const struct CH32V30x_I2C_Init_info *dev_info = &(i2c_dev_info[iic_id]);
    RCC_APB2PeriphClockCmd(dev_info->SCL_RCC_APB2Periph_id, ENABLE);
    RCC_APB2PeriphClockCmd(dev_info->SDA_RCC_APB2Periph_id, ENABLE);
    RCC_APB1PeriphClockCmd(dev_info->I2C_RCC_APB1Periph_id, ENABLE);
    GPIO_InitTypeDef i2c_gpio_config = 
    {
        .GPIO_Mode = GPIO_Mode_AF_OD,
        .GPIO_Speed = GPIO_Speed_2MHz
    };
    i2c_gpio_config.GPIO_Pin = dev_info->SCL_GPIO_Pin;
    GPIO_Init(dev_info->SCL_GPIO_Port, &i2c_gpio_config);
    i2c_gpio_config.GPIO_Pin = dev_info->SDA_GPIO_Pin;
    GPIO_Init(dev_info->SDA_GPIO_Port, &i2c_gpio_config);
    I2C_InitTypeDef i2c_init_config = 
    {
        .I2C_Mode = I2C_Mode_I2C,
        .I2C_ClockSpeed = baud,
        .I2C_DutyCycle = I2C_DutyCycle_2,
        .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit,
        .I2C_Ack = I2C_Ack_Enable,
    };

    if(dev_info->i2c_dev_handle->CTLR1 & I2C_CTLR1_PE)
    {
        LOGW("I2C DEV %d has enabled", iic_id);
    }
    I2C_Init(dev_info->i2c_dev_handle, &i2c_init_config);
    LOGI("Init iic %d,baud %d,SDA %d:%d,SCL %d:%d", iic_id, baud, dev_info->SDA_GPIO_Port, dev_info->SDA_GPIO_Pin, dev_info->SCL_GPIO_Port, dev_info->SCL_GPIO_Pin);
    return 0;
}
static int hal_iic_send_bytes(int iic_id, uint8_t *tx_buf, uint8_t num)
{
    int ret = 0;
    if(iic_id > MAX_IIC_DEV_NUM - 1)
    {
        LOGE("id exceed max iic dev num");
        return -ENXIO;
    }
    I2C_TypeDef *i2c_dev = (&(i2c_dev_info[iic_id]))->i2c_dev_handle;
    while(I2C_GetFlagStatus(i2c_dev, I2C_FLAG_TXE) == 0);
    for(int i=0; i<num; i++)
    {
        I2C_SendData(i2c_dev, tx_buf[i]);
        if(ch32v30x_i2c_wait_tx_free(i2c_dev) < 0)
        {
            ret = -i;
            goto iic_send_bytes_exit;
        }
    }
iic_send_bytes_exit:
    return ret;
}
static int hal_iic_read_bytes(int iic_id, uint8_t *rx_buf, uint8_t max_len)
{
    int ret = 0;
    if(iic_id > MAX_IIC_DEV_NUM - 1)
    {
        LOGE("id exceed max iic dev num");
        return -ENXIO;
    }
    I2C_TypeDef *i2c_dev = (&(i2c_dev_info[iic_id]))->i2c_dev_handle;
    while(max_len)
    {
        if(max_len == 1)
        {
            I2C_AcknowledgeConfig(i2c_dev, DISABLE);
        }
        if(ch32v30x_i2c_wait_rx_complete(i2c_dev) < 0)
        {
            ret = -max_len;
            goto iic_read_bytes_exit;
        }
        *rx_buf++ = I2C_ReceiveData(i2c_dev);
        max_len--;
    }
iic_read_bytes_exit:
    return ret;
}
error_t hal_iic_write_nbytes_to_device_reg(int iic_id, uint16_t dev_addr, uint8_t* reg_addr, uint8_t reg_addr_len, uint8_t* tx_data, uint8_t tx_len)
{
    error_t ret = 0;
    if(iic_id > MAX_IIC_DEV_NUM - 1)
    {
        LOGE("id exceed max iic dev num");
        return -ENXIO;
    }
    const struct CH32V30x_I2C_Init_info * dev_info = &(i2c_dev_info[iic_id]);
    I2C_TypeDef *i2c_dev = dev_info->i2c_dev_handle;
    int addr_len = hal_iic_get_dev_addr_len(i2c_dev);
    if(addr_len < 0)
    {
        return addr_len;
    }
    switch(addr_len)
    {
        case 7:
            break;
        case 10:
        default:
            LOGE("10bit i2c no supported");
            return -EINVAL;
            break;
    }
    I2C_GenerateSTART(i2c_dev, ENABLE);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_MODE_SELECT))
    {
        return -EBUSY;
    }
    I2C_Send7bitAddress(i2c_dev, (dev_addr & 0x00ff) << 1, I2C_Direction_Transmitter);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        return -EBUSY;
    }
    if((ret = hal_iic_send_bytes(iic_id, reg_addr, reg_addr_len)) < 0)
    {
        LOGE("I2C Send reg addr %04X fail" ,*((uint16_t*)reg_addr));
        ret = -EBUSY;
        goto iic_write_nbytes_exit;
    }
    if((ret = hal_iic_send_bytes(iic_id, reg_addr, reg_addr_len)) < 0)
    {
        LOGE("I2C Send data %02X to reg %04X faid" ,tx_data[-ret], *((uint16_t*)reg_addr));
        ret = -EBUSY;
        goto iic_write_nbytes_exit;
    }
iic_write_nbytes_exit:
    I2C_GenerateSTOP(i2c_dev, ENABLE);
    return ret;
}
error_t hal_iic_read_nbytes_form_reg(int iic_id, uint16_t dev_addr, uint8_t* reg_addr, uint8_t reg_addr_len, uint8_t* rx_data, uint8_t rx_max_len)
{
    error_t ret = 0;
    if(iic_id > MAX_IIC_DEV_NUM - 1)
    {
        LOGE("id exceed max iic dev num");
        return -ENXIO;
    }
    const struct CH32V30x_I2C_Init_info * dev_info = &(i2c_dev_info[iic_id]);
    I2C_TypeDef *i2c_dev = dev_info->i2c_dev_handle;
    int addr_len = hal_iic_get_dev_addr_len(i2c_dev);
    if(addr_len < 0)
    {
        return addr_len;
    }
    switch(addr_len)
    {
        case 7:
            break;
        case 10:
        default:
            LOGE("10bit i2c no supported");
            return -EINVAL;
            break;
    }
    I2C_GenerateSTART(i2c_dev, ENABLE);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_MODE_SELECT))
    {
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }
    I2C_Send7bitAddress(i2c_dev, (dev_addr & 0x00ff) << 1, I2C_Direction_Transmitter);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }
    if((ret = hal_iic_send_bytes(iic_id, reg_addr, reg_addr_len)) < 0)
    {
        LOGE("I2C Send reg addr %04X fail" ,*((uint16_t*)reg_addr));
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }
    I2C_GenerateSTART(i2c_dev, ENABLE);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_MODE_SELECT))
    {
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }
    I2C_Send7bitAddress(i2c_dev, (dev_addr & 0x00ff) << 1, I2C_Direction_Receiver);
    if(ch32v30x_i2c_wait_event(i2c_dev, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }
    if((ret = hal_iic_read_bytes(iic_id, rx_data, rx_max_len)) < 0)
    {
        LOGE("I2C read %d data fail in %04d" , rx_max_len + ret, *((uint16_t*)reg_addr));
        ret = -EBUSY;
        goto iic_read_nbytes_exit;
    }

iic_read_nbytes_exit:
    I2C_GenerateSTOP(i2c_dev, ENABLE);
    return ret;
}

